package ldh.serialize;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import ldh.serialize.io.ByteBuf;
import ldh.serialize.register.Register;
import ldh.serialize.util.SerializedUtil;

public class SerializedItem<O, T> {

	private LazyMethod<O, T> proxyObj;
	
	private Serialized<T> serialized;
	
	private Method readMethod;
	
	private Method writeMethod;
	
	private Field field;
	
	private String propertyName;

	private Class clazz;

	private Method method;
	
	public SerializedItem(LazyMethod<O, T> proxyObj, Serialized<T> serialized) {
		this.proxyObj = proxyObj;
		this.serialized = serialized;
		this.propertyName = proxyObj.getPropertyName();
	}

	public SerializedItem(Class clazz) {
		this.clazz = clazz;
		this.serialized = (Serialized<T>) Register.getInstance().getSerialized(clazz);
	}

	public SerializedItem(Method method) {
		this.method = method;
		this.serialized = (Serialized<T>) Register.getInstance().getSerialized(clazz);
	}
	
	public SerializedItem(Method getMethod, Method setMethod, Serialized<T> serialized) {
		this.readMethod = getMethod;
		this.writeMethod = setMethod;
		this.serialized = serialized;
		this.propertyName = getMethod.getName().substring(3);
		this.propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
	}
	
	public SerializedItem(Field field, Serialized<T> serialized) {
		this.field = field;
		this.serialized = serialized;
		this.propertyName = field.getName();
	}
	
	public String getName() {
		return propertyName;
	}
	
	public void write(ByteBuf out, O o) throws IOException {
		if (!(serialized instanceof BinarySerialized)) {
			throw new RuntimeException("serialized is not BinarySerialized");
		}
		BinarySerialized<T> binarySerialized = (BinarySerialized<T>) serialized;
		T obj = null;
		if (proxyObj != null) {
			obj = proxyObj.getValue(o);
		} else if (field != null) {
			obj = SerializedUtil.getValue(o, field);
		} else if (clazz != null) {
			obj = (T) o;
		} else {
			obj = SerializedUtil.getValue(o, readMethod, null);
		}
		binarySerialized.write(out, obj);
	}
	
	public O read(O o, ByteBuf in) throws IOException {
		if (!(serialized instanceof BinarySerialized)) {
			throw new RuntimeException("serialized is not BinarySerialized");
		}
		BinarySerialized<T> binarySerialized = (BinarySerialized<T>) serialized;
		T value = binarySerialized.read(in);
		if (proxyObj != null) {
			proxyObj.setValue(o, value);
		} else if (field != null) {
			SerializedUtil.setValue(o, field, value);
		} else if (clazz != null) {
			return (O) value;
		} else {
			SerializedUtil.setValue(o, writeMethod, value);
		}
		return o;
	}
	
	public String toString(O o) {
		if (!(serialized instanceof StringSerialized)) {
			throw new RuntimeException("serialized is not StringSerialized");
		}
		StringSerialized<T> stringSerialized = (StringSerialized<T>) serialized;
		stringSerialized.setPropertyName(this.getName());
		T obj = null;
		if (proxyObj != null) {
			obj = proxyObj.getValue(o);
		} else if (field != null) {
			obj = SerializedUtil.getValue(o, field);
		} else {
			obj = SerializedUtil.getValue(o, readMethod, null);
		}
		StringWriter writer = new StringWriter();
		stringSerialized.writeToString(writer, obj);
		return writer.toString();
	}
	
	public O toObject(O o, String str) throws IOException {
		if (!(serialized instanceof StringSerialized)) {
			throw new RuntimeException("serialized is not StringSerialized");
		}
		StringSerialized<T> stringSerialized = (StringSerialized<T>) serialized;
		StringReader reader = new StringReader(str);
		T value = stringSerialized.readFromString(reader);
		if (proxyObj != null) {
			proxyObj.setValue(o, value);
		} else if (field != null) {
			SerializedUtil.setValue(o, field, value);
		} else {
			SerializedUtil.getValue(o, writeMethod, value);
		}
		return o;
	}
	
	public Object getValue(O o) {
		return proxyObj.getValue(o);
	}
	
}
